//
//  StatzSink.m
//  Statz
//
//  Created by Dave MacLachlan on 07/07/23.
//
// Copyright 2007 Google Inc.
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations under
// the License.
//

#import "StatzSink.h"
#import "AppDelegate.h"
#import "Status.h"

NSString *const StatzStateChangedNotification = @"StatzStateChangedNotification";
NSString *const StatzStateChangedState = @"StatzStateChangedState";  // NSNumber

static const int kStatzStatusSinkPluginCoderVersion1 = 1;
static const int kStatzStatusSinkPluginCoderVersion2 = 2;

static NSString *const kStatzStatusSinkPluginCoderVersionKey = @"kStatzStatusSinkPluginCoderVersionKey";
static NSString *const kStatzStatusSinkPluginClassKey = @"kStatzStatusSinkPluginClassKey";
static NSString *const kStatzStatusSinkPluginEnabledKey = @"kStatzStatusSinkPluginEnabledKey";
static NSString *const kStatzStatusSinkPluginStateKey = @"kStatzStatusSinkPluginStateKey";
static NSString *const kStatzStatusSinkPluginUUIDKey = @"kStatzStatusSinkPluginUUIDKey";

typedef struct {
  StatzState state;
  NSImage *icon;
  NSString *name;
} StateStorage;

#define kStateCount 7
static StateStorage gStates[kStateCount] = { {kStatzStateNone} };

@implementation StatzSink

+ (void)initialize {
  if (gStates[0].state == kStatzStateNone) {
    gStates[0].state = kStatzStateAvailable;
    gStates[0].icon = [NSImage imageNamed:@"available"];
    gStates[0].name = NSLocalizedString(@"Available", @"state string");
    gStates[1].state = kStatzStateAway;
    gStates[1].icon = [NSImage imageNamed:@"away"];
    gStates[1].name = NSLocalizedString(@"Away", @"state string");
    gStates[2].state = kStatzStateInvisible;
    gStates[2].icon = [NSImage imageNamed:@"invisible"];
    gStates[2].name = NSLocalizedString(@"Invisible", @"state string");
    gStates[3].state = kStatzStateOffline;
    gStates[3].icon = [NSImage imageNamed:@"offline"];
    gStates[3].name = NSLocalizedString(@"Offline", @"state string");
    gStates[4].state = kStatzStateDoNotDisturb;
    gStates[4].icon = [NSImage imageNamed:@"donotdisturb"];
    gStates[4].name = NSLocalizedString(@"Do Not Disturb", @"state string");
    gStates[5].state = kStatzStateContactMe;
    gStates[5].icon = [NSImage imageNamed:@"contactme"];
    gStates[5].name = NSLocalizedString(@"Contact Me", @"state string");
    gStates[6].state = kStatzStateMixed;
    gStates[6].icon = [NSImage imageNamed:@"mixed"];
    gStates[6].name = NSLocalizedString(@"mixed", @"state string");
  }
}

+ (NSString*)stringForState:(StatzState)state {
  NSString *value = nil;
  for (int i = 0; i < kStateCount; ++i) {
    if (gStates[i].state == state) {
      value = gStates[i].name;
      break;
    }
  }
  return value;
}

+ (NSImage*)iconForState:(StatzState)state {
  NSImage *value = nil;
  for (int i = 0; i < kStateCount; ++i) {
    if (gStates[i].state == state) {
      value = gStates[i].icon;
      break;
    }
  }
  return value;
}

+ (NSMenu*)stateMenuWithStates:(int)states target:(id)target {
  NSMenu *menu = [[[NSMenu alloc] initWithTitle:@"State"] autorelease];
  for (int i = 0; i < kStateCount; ++i) {
    if ((gStates[i].state & states) != 0) {
      NSMenuItem *item = [menu addItemWithTitle:gStates[i].name
                                         action:@selector(stateSelected:)
                                  keyEquivalent:@""];
      [item setTarget:target];
      [item setImage:gStates[i].icon];
      [item setTag:gStates[i].state];
    }
  }
  return menu;
}
       
- (id)init {
  [NSException raise:NSInternalInconsistencyException format:@"Can't call Init on a StatzSink"];
  return nil;
}


- (id)initWithPlugin:(id<StatzSinkPlugin>)plugin {
  if ((self = [super init])) {
    plugin_ = [plugin retain];
  }
  if (!plugin_) {
    [self release];
    self = nil;
  } else {
    StatzSource *defaultSource = [[NSApp delegate] defaultStatusSource];
    [statuses_ addObject:[[[Status alloc] initWithStatus:[defaultSource nameToken]] autorelease]];
    if ([plugin_ stateMenuMask] != kStatzStateNone) {
      state_ = kStatzStateAvailable;
    } else {
      state_ = kStatzStateNone;
    }
  }
  return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
  if ((self = [super initWithCoder:aDecoder])) {
    int version = [aDecoder decodeInt32ForKey:kStatzStatusSinkPluginCoderVersionKey];
    if (version != kStatzStatusSinkPluginCoderVersion1 &&
        version != kStatzStatusSinkPluginCoderVersion2) {
      [self release];
      return nil;
    }
    NSEnumerator *pluginEnum = [[[NSApp delegate] plugins] objectEnumerator];
    NSString *value = nil;
    if (version == kStatzStatusSinkPluginCoderVersion1) {
      value = [aDecoder decodeObjectForKey:kStatzStatusSinkPluginClassKey];
    } else {
      value = [aDecoder decodeObjectForKey:kStatzStatusSinkPluginUUIDKey];
    }
    id<StatzSinkPlugin> plugin = nil;
    while ((plugin = [pluginEnum nextObject])) {
      BOOL isGood = NO;
      if (version == kStatzStatusSinkPluginCoderVersion1) {
        isGood = [value isEqualToString:NSStringFromClass([plugin class])];
      } else {
        isGood = [value isEqualToString:[plugin uuid]];
      }
      if (isGood) {
        plugin_ = [plugin retain];
        break;
      }
    }
    enabled_ = [aDecoder decodeBoolForKey:kStatzStatusSinkPluginEnabledKey];
    state_ = [aDecoder decodeInt32ForKey:kStatzStatusSinkPluginStateKey];
  }
  return self;
}

- (void)dealloc {
  [stateMenu_ release];
  [plugin_ release];
  [super dealloc];
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
  [super encodeWithCoder:aCoder];
  [aCoder encodeInt32:kStatzStatusSinkPluginCoderVersion2
               forKey:kStatzStatusSinkPluginCoderVersionKey];
  [aCoder encodeObject:[plugin_ uuid] forKey:kStatzStatusSinkPluginUUIDKey];
  [aCoder encodeBool:enabled_ forKey:kStatzStatusSinkPluginEnabledKey];
  [aCoder encodeInt32:state_ forKey:kStatzStatusSinkPluginStateKey];
}

- (NSString *)lastKnownStatus {
  return [[lastKnownStatus_ retain] autorelease];
}

- (NSImage *)image {  
  return [plugin_ image];
}

- (void)setLastKnownStatus:(NSString *)value {
  if (lastKnownStatus_ != value) {
    [lastKnownStatus_ autorelease];
    lastKnownStatus_ = [value copy];
  }
}

- (BOOL)isNameEditable {
  return NO;
}

- (BOOL)isValid {
  return plugin_ != nil;
}

- (BOOL)isEnabled {
  return enabled_;
}

- (void)setEnabled:(BOOL)enabled {
  enabled_ = enabled;
}

- (BOOL)hasPreferences {
  return [plugin_ hasPreferences];
}

- (NSMenu*)stateMenu {
  if (!stateMenu_) {
    unsigned int menuMask = [plugin_ stateMenuMask];
    if (menuMask != kStatzStateNone) {
      stateMenu_ = [[StatzSink stateMenuWithStates:menuMask
                                            target:self] retain];
      
    }
  }
  return stateMenu_;
}

- (NSString*)name {
  return [plugin_ name];
}

- (void)updateStatusThread:(StatusUpdate*)sender {
  [plugin_ updateStatusThread:sender];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
  SEL aSelector = [invocation selector];
  
  if ([plugin_ respondsToSelector:aSelector])
    [invocation invokeWithTarget:plugin_];
  else
    [self doesNotRecognizeSelector:aSelector];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
  NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
  if (!signature) {
    signature = [plugin_ methodSignatureForSelector:aSelector];
  }
  return signature;
}

- (id<StatzSinkPlugin>)plugin {
  return plugin_;
}

- (NSAttributedString *)displayNameAndStatus {
  NSMutableAttributedString *mutableString = [[[NSMutableAttributedString alloc] init] autorelease];
  StatzState state = [self state];
  if (state != kStatzStateNone) {
    NSString * imgName = @"my_image.tif";
    NSFileWrapper *fwrap = [[[NSFileWrapper alloc] initRegularFileWithContents:
                            [[StatzSink iconForState:state] TIFFRepresentation]] autorelease];
    [fwrap setFilename:imgName];
    [fwrap setPreferredFilename:imgName];
    
    // Create an attachment with the file wrapper
    NSTextAttachment * ta = [[[NSTextAttachment alloc] initWithFileWrapper:fwrap] autorelease];
    NSAttributedString *attachment = [NSAttributedString attributedStringWithAttachment:ta];
    [mutableString appendAttributedString:attachment];
  }
  [mutableString appendAttributedString:[super displayNameAndStatus]];
  return mutableString;
}

- (StatzState)state {
  return state_;
}

- (void)setState:(StatzState)state {
  state &= [plugin_ stateMenuMask];
  if (state != state_) {
    state_ = state;
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc postNotificationName:StatzStateChangedNotification
                      object:self
                    userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:state]
                                                         forKey:StatzStateChangedState]];
  }
}

- (void)stateSelected:(id)sender {
  [self setState:[sender tag]];
}


@end
